package com.yshy.moviechoseseatingcommon.util;

import cn.hutool.core.util.StrUtil;
import cn.hutool.http.HttpRequest;
import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONObject;
import com.yshy.moviechoseseatingcommon.entity.bo.cinemas.GetByIdBo;
import com.yshy.moviechoseseatingcommon.entity.bo.cinemas.V1PagedQueryBo;
import com.yshy.moviechoseseatingcommon.entity.bo.cinemas.V2PagedQueryBo;
import com.yshy.moviechoseseatingcommon.entity.bo.cities.ListByProvinceCodeBo;
import com.yshy.moviechoseseatingcommon.entity.bo.cities.LocateByLongitudeAndLatitudeBo;
import com.yshy.moviechoseseatingcommon.entity.bo.films.*;
import com.yshy.moviechoseseatingcommon.entity.bo.orders.*;
import com.yshy.moviechoseseatingcommon.entity.bo.regions.ListByCityIdBo;
import com.yshy.moviechoseseatingcommon.entity.bo.shows.*;
import com.yshy.moviechoseseatingcommon.entity.dto.ComResponseDto;
import com.yshy.moviechoseseatingcommon.entity.vo.cinemas.GetByIdVo;
import com.yshy.moviechoseseatingcommon.entity.vo.cinemas.ListAllBrandsVo;
import com.yshy.moviechoseseatingcommon.entity.vo.cinemas.V1PagedQueryVo;
import com.yshy.moviechoseseatingcommon.entity.vo.cinemas.V2PagedQueryVo;
import com.yshy.moviechoseseatingcommon.entity.vo.cities.*;
import com.yshy.moviechoseseatingcommon.entity.vo.films.*;
import com.yshy.moviechoseseatingcommon.entity.vo.orders.*;
import com.yshy.moviechoseseatingcommon.entity.vo.regions.ListByCityIdVo;
import com.yshy.moviechoseseatingcommon.entity.vo.shows.*;
import com.yshy.moviechoseseatingcommon.properties.DockingInterfaceProperties;
import com.yshy.moviechoseseatingcommon.properties.NoticeProperties;
import lombok.Data;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;

import java.nio.charset.StandardCharsets;
import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException;
import java.util.*;

/**
 * @author :
 * @desc :
 * @create: 下午2:47
 **/
@Data
@Component
@Slf4j
public class RequstUtil {
    @Autowired
    DockingInterfaceProperties dockingInterfaceProperties;
    @Autowired
    NoticeProperties noticeProperties;

    /**
     * 查询中国大陆所有城市
     *
     * @param
     * @return
     */
    public V1ListAllVo citiesV1ListAll() {
        return httpRequest(dockingInterfaceProperties.getUrl().getCitiesV1CitiesListAll(), "{}", V1ListAllVo.class);
    }

    /**
     * 查询中国大陆所有城市V2
     *
     * @param
     * @return
     */
    public V2ListAllVo citiesV2ListAll() {
        return httpRequest(dockingInterfaceProperties.getUrl().getCitiesV2CinemasListAll(), "{}", V2ListAllVo.class);
    }

    /**
     * 根据经纬度定位城市
     *
     * @param
     * @return
     */
    public LocateByLongitudeAndLatitudeVo citiesLocateByLongitudeAndLatitude(LocateByLongitudeAndLatitudeBo bo) {
        return httpRequest(dockingInterfaceProperties.getUrl().getCitiesCitiesLocateByLongitudeAndLatitude(), JSONObject.toJSONString(bo), LocateByLongitudeAndLatitudeVo.class);
    }

    /**
     * 获取中国大陆省份
     *
     * @param
     * @return
     */
    public ProvincesListAllVo citiesProvincesListAll() {
        return httpRequest(dockingInterfaceProperties.getUrl().getCitiesProvincesListAll(), "{}", ProvincesListAllVo.class);
    }

    /**
     * 根据省份编号获取城市
     *
     * @param
     * @return
     */
    public ListByProvinceCodeVo citiesListByProvinceCode(ListByProvinceCodeBo bo) {
        return httpRequest(dockingInterfaceProperties.getUrl().getCitiesCitiesListByProvinceCode(), JSONObject.toJSONString(bo), ListByProvinceCodeVo.class);
    }


    /**
     * 查询城市区域
     *
     * @param
     * @return
     */
    public ListByCityIdVo regionsListByCityId(ListByCityIdBo bo) {
        return httpRequest(dockingInterfaceProperties.getUrl().getRegionsRegionsListByCityId(), JSONObject.toJSONString(bo), ListByCityIdVo.class);
    }


    /**
     * 根据影院id查询影院
     */
    public GetByIdVo cinemasGetById(GetByIdBo bo) {
        return httpRequest(dockingInterfaceProperties.getUrl().getCinemasGetById(), JSONObject.toJSONString(bo), GetByIdVo.class);
    }

    /**
     * 分页查询城市影院(v1)
     */
    public V1PagedQueryVo cinemasV1PagedQuery(V1PagedQueryBo bo) {
        return httpRequest(dockingInterfaceProperties.getUrl().getCinemasV1PagedQuery(), JSONObject.toJSONString(bo), V1PagedQueryVo.class);
    }

    /**
     * 分页查询城市影院(v2)
     */
    public V2PagedQueryVo cinemasV2PagedQuery(V2PagedQueryBo bo) {
        return httpRequest(dockingInterfaceProperties.getUrl().getCinemasV2PagedQuery(), JSONObject.toJSONString(bo), V2PagedQueryVo.class);
    }

    /**
     * 查询所有影院品牌
     */
    public ListAllBrandsVo cinemasListAllBrands() {
        return httpRequest(dockingInterfaceProperties.getUrl().getCinemasListAll(), "{}", ListAllBrandsVo.class);
    }


    /**
     * 查询影院近期所有场次
     */
    public ListByCinemaIdVo showsListByCinemaId(ListByCinemaIdBo bo) {
        return httpRequest(dockingInterfaceProperties.getUrl().getShowsListByCinemaId(), JSONObject.toJSONString(bo), ListByCinemaIdVo.class);
    }

    /**
     * 根据城市和影片查询场次
     */
    public ListByCityAndFilmVo showsListByCityAndFilm(ListByCityAndFilmBo bo) {
        return httpRequest(dockingInterfaceProperties.getUrl().getShowsListByCityAndFilm(), JSONObject.toJSONString(bo), ListByCityAndFilmVo.class);
    }

    /**
     * 根据城市查询场次
     */
    public ListsByCityIdVo showsListByCityId(ListsByCityIdBo bo) {
        return httpRequest(dockingInterfaceProperties.getUrl().getShowsListByCityId(), JSONObject.toJSONString(bo), ListsByCityIdVo.class);
    }

    /**
     * 根据影院和影片查询场次
     */
    public ListByCinemaAndFilmVo showsListByCinemaAndFilm(ListByCinemaAndFilmBo bo) {
        return httpRequest(dockingInterfaceProperties.getUrl().getShowsListByCinemaAndFilm(), JSONObject.toJSONString(bo), ListByCinemaAndFilmVo.class);
    }

    /**
     * 查询场次实时座位
     */
    public GetRealTimeSeatByShowIdVo showsGetRealTimeSeatByShowId(GetRealTimeSeatByShowIdBo bo) {
        return httpRequest(dockingInterfaceProperties.getUrl().getShowsGetRealTimeSeatByShowId(), JSONObject.toJSONString(bo), GetRealTimeSeatByShowIdVo.class);
    }

    /**
     * 分页查询场次
     */
    public PagedQueryVo showsPagedQuery(PagedQueryBo bo) {
        return httpRequest(dockingInterfaceProperties.getUrl().getShowsPagedQuery(), JSONObject.toJSONString(bo), PagedQueryVo.class);
    }

    /**
     * 按城市、影院、影片查询场次日期
     */
    public V1ListShowDateVo showsV1ListShowDate(V1ListShowDateBo bo) {
        return httpRequest(dockingInterfaceProperties.getUrl().getShowsV1ListShowDate(), JSONObject.toJSONString(bo), V1ListShowDateVo.class);
    }

    /**
     * 按城市、影院、影片查询场次日期V2
     */
    public V2ListShowDateVo showsV2ListShowDate(V2ListShowDateBo bo) {
        return httpRequest(dockingInterfaceProperties.getUrl().getShowsV2ListShowDate(), JSONObject.toJSONString(bo), V2ListShowDateVo.class);
    }


    /**
     * 查询所有影片
     */
    public ListAllVo filmsListAll() {
        return httpRequest(dockingInterfaceProperties.getUrl().getFilmsListAll(), "{}", ListAllVo.class);
    }

    /**
     * 根据城市id查询影片
     */
    public ListByCityIdsVo filmsListByCityId(ListByCityIdsBo bo) {
        return httpRequest(dockingInterfaceProperties.getUrl().getFilmsListByCityId(), JSONObject.toJSONString(bo), ListByCityIdsVo.class);
    }

    /**
     * 根据影院id查询影片
     */
    public ListsByCinemaIdVo filmsListByCinemaId(ListByCinemaIdsBo bo) {
        return httpRequest(dockingInterfaceProperties.getUrl().getFilmsListByCinemaId(), JSONObject.toJSONString(bo), ListsByCinemaIdVo.class);
    }

    /**
     * 根据城市分页查询影片
     */
    public PagedQuerysVo filmsPagedQuery(PagedQuerysBo bo) {
        return httpRequest(dockingInterfaceProperties.getUrl().getFilmsPagedQuery(), JSONObject.toJSONString(bo), PagedQuerysVo.class);
    }

    /**
     * 分页查询待上映影片
     */
    public PagedQueryNotSHownFilmsVo filmsPagedQueryNotShownFilms(PagedQueryNotShownFilmsBo bo) {
        return httpRequest(dockingInterfaceProperties.getUrl().getFilmsPagedQueryNotShownFilms(), JSONObject.toJSONString(bo), PagedQueryNotSHownFilmsVo.class);
    }

    /**
     * 查询影片id查询影片
     */
    public GetByIdsVo filmsGetById(GetByIdsBo bo) {
        return httpRequest(dockingInterfaceProperties.getUrl().getFilmsGetById(), JSONObject.toJSONString(bo), GetByIdsVo.class);
    }


    /**
     * 创建订单(备用金支付)
     */
    public CreateMovieOrderVo ordersCreateMovieOrder(CreateMovieOrderBo bo) {
        return httpRequest(dockingInterfaceProperties.getUrl().getOrdersCreateMovieOrder(), JSONObject.toJSONString(bo), CreateMovieOrderVo.class);
    }

    /**
     * 订单退款
     */
    public ComResponseDto ordersRefundMovieOrder(RefundMovieOrderBo bo) {
        return httpRequest(dockingInterfaceProperties.getUrl().getOrdersRefundMovieOrder(), JSONObject.toJSONString(bo), ComResponseDto.class);
    }

    /**
     * 根据订单号查询订单
     */
    public GetByOrderNoVo ordersGetByOrderNo(GetByOrderNoBo bo) {
        return httpRequest(dockingInterfaceProperties.getUrl().getOrdersGetByOrderNo(), JSONObject.toJSONString(bo), GetByOrderNoVo.class);
    }

    /**
     * 根据平台订单号查询订单
     */
    public GetByPlatformOrderNoVo ordersGetByPlatformOrderNo(GetByPlatformOrderNoBo bo) {
        return httpRequest(dockingInterfaceProperties.getUrl().getOrdersGetByPlatformOrderNo(), JSONObject.toJSONString(bo), GetByPlatformOrderNoVo.class);
    }

    /**
     * 根据批量订单号查询订单
     */
    public ListByBatchOrderNosVo ordersListByBatchOrderNos(ListByBatchOrderNosBo bo) {
        return httpRequest(dockingInterfaceProperties.getUrl().getOrdersListByBatchOrderNos(), JSONObject.toJSONString(bo), ListByBatchOrderNosVo.class);
    }

    /**
     * 分页查询订单
     */
    public PagedsQueryVo ordersPagedQuery(PagedsQueryBo bo) {
        return httpRequest(dockingInterfaceProperties.getUrl().getMovieOrderPagedQuery(), JSONObject.toJSONString(bo), PagedsQueryVo.class);
    }


    /**
     * 通知订单状态
     */
    public String notice(NoticeCallbackBo bo) {
        return httpRequests(noticeProperties.getUrl(), bo);
    }

    /**
     * 调用接口
     *
     * @param <T>
     * @return
     */
    private <T> T httpRequest(String url, String param, Class<T> clazz) {
        try {
            Long timestamp = new Date().getTime() / 1000;
            // 解析 JSON 字符串
            JSONObject jsonObject = JSONObject.parseObject(param);
            // 添加新字段
            jsonObject.put("platformId", dockingInterfaceProperties.getPlatformId());
            jsonObject.put("timestamp", timestamp);
            // 转换回 JSON 字符串
            String params = jsonObject.toJSONString();
            String paramStr = sortJsonKeys(params) + dockingInterfaceProperties.getSecret();
            String sign = md5(paramStr);
            // 添加新字段
            jsonObject.put("sign", sign);
            String paramEnd = jsonObject.toJSONString();
            log.info("接口请求地址：{}", url);
            log.info("接口请求参数：{}", paramEnd);
            String result = HttpRequest
                    .post(url)
                    .header("Content-Type", "application/json")
                    .body(paramEnd)
                    .execute().body();
            if (StrUtil.isBlank(result)) {
                return null;
            }
            log.info("接口请求返回结果：{}", result);
            return JSONObject.parseObject(result, clazz);
        } catch (Exception exception) {
            throw new RuntimeException("接口请求异常或解析异常");
        }
    }
    private String httpRequests(String url, NoticeCallbackBo bo) {
        try {
            log.info("接口请求地址：{}", url);
            log.info("接口请求参数：{}", JSONObject.toJSONString(bo));
            String sign = Md5Util.checkSign(bo, noticeProperties.getAppKey(), noticeProperties.getSecretKey());
            bo.setSign(sign);
            String result = HttpRequest
                    .post(url)
                    .header("Content-Type", "application/json")
                    .body(JSONObject.toJSONString(bo))
                    .execute().body();
            if (StrUtil.isBlank(result)) {
                return null;
            }
            log.info("接口请求返回结果：{}", result);
            return result;
        } catch (Exception exception) {
            throw new RuntimeException("接口请求异常或解析异常");
        }
    }
    /**
     * 解析和排序 JSON 对象，并将排序后的键值对以 & 分隔
     *
     * @param params
     * @return
     */
    public static String sortJsonKeys(String params) {
        // 解析 JSON 字符串
        JSONObject jsonObject = JSON.parseObject(params);

        // 提取键值对并排序
        List<Map.Entry<String, Object>> entries = new ArrayList<>();
        Set<Map.Entry<String, Object>> entrySet = jsonObject.entrySet();
        for (Map.Entry<String, Object> entry : entrySet) {
            entries.add(entry);
        }

        // 排序
        Collections.sort(entries, new Comparator<Map.Entry<String, Object>>() {
            @Override
            public int compare(Map.Entry<String, Object> o1, Map.Entry<String, Object> o2) {
                return o1.getKey().compareTo(o2.getKey());
            }
        });

        // 构建新的字符串
        StringBuilder result = new StringBuilder();
        for (int i = 0; i < entries.size(); i++) {
            Map.Entry<String, Object> entry = entries.get(i);
            result.append(entry.getKey()).append("=").append(entry.getValue());
            if (i < entries.size() - 1) {
                result.append("&");
            }
        }

        return result.toString();
    }

    /**
     * 将字符串进行MD5加密
     *
     * @param content 要加密的内容
     * @return 加密后的内容
     * @throws NoSuchAlgorithmException
     */
    public static String md5(String content) throws NoSuchAlgorithmException {
//        MessageDigest md = MessageDigest.getInstance("MD5");
//        md.update(content.getBytes(Charset.forName("utf-8")));
//        byte[] digest = md.digest();
//        return DatatypeConverter.printHexBinary(digest);
        try {
            MessageDigest md = MessageDigest.getInstance("MD5");
            md.update(content.getBytes(StandardCharsets.UTF_8));
            byte[] digest = md.digest();
            return bytesToHex(digest);
        } catch (NoSuchAlgorithmException e) {
            throw new RuntimeException("MD5 algorithm not found", e);
        }
    }
    private static String bytesToHex(byte[] bytes) {
        StringBuilder hexString = new StringBuilder();
        for (byte b : bytes) {
            String hex = Integer.toHexString(0xff & b);
            if (hex.length() == 1) {
                hexString.append('0');
            }
            hexString.append(hex);
        }
        return hexString.toString();
    }
}
